//
//  CalculatorBrain.m
//  Calculator
//
//  Created by Willem van Willigen on 06/11/2010.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "CalculatorBrain.h"
#define VARIABLE_PREFIX @"%"

@implementation CalculatorBrain



@synthesize operand;

- (id)init {
	if( self = [super init]) {
		internalExpression = [[NSMutableArray alloc] init];
	}
	return self;
}

- (void)performWaitingOperation {
	if ([@"+" isEqual:waitingOperation]) {
		operand = waitingOperand + operand;
	} else if([@"-" isEqual:waitingOperation]) {
		operand = waitingOperand - operand;
	} else if([@"*" isEqual:waitingOperation]) {
		operand = waitingOperand * operand;
	} else if([@"/" isEqual:waitingOperation]) {
		if(operand) {
			operand = waitingOperand / operand;
		}
	}
}

- (void)setOperand:(double)aDouble {
	operand = aDouble;
	[internalExpression addObject:[NSNumber numberWithDouble:aDouble]];
}
   

- (void)setVariableAsOperand:(NSString *)variableName {
	[internalExpression addObject:[VARIABLE_PREFIX stringByAppendingString:variableName]];
}

- (double)performOperation:(NSString *)operation {
	if([operation isEqual:@"sqrt"]) {
		operand = sqrt(operand);
		[internalExpression addObject:operation];
	} else if([operation isEqual:@"1/x"]) {
		if(operand) {
			operand = 1/operand;
		} 
		if (operand || [CalculatorBrain variablesInExpression:internalExpression]) {
			[internalExpression addObject:operation];
		}
	} else if([operation isEqual:@"+/-"]) {
		operand = -operand;
	} else if([operation isEqual:@"sin"]) {
		operand = sin(operand);
		[internalExpression addObject:operation];
	} else if([operation isEqual:@"cos"]) {
		operand = cos(operand);
		[internalExpression addObject:operation];
	} else if([operation isEqual:@"Store"]) {
		memoryOperand = operand;
	} else if([operation isEqual:@"Recall"]) {
		operand = memoryOperand;
	} else if([operation isEqual:@"Mem+"]) {
		memoryOperand += operand;
	} else if([operation isEqual:@"c"]) {
		operand = memoryOperand = waitingOperand = 0;
		[waitingOperation release];
		waitingOperation = nil;
		[internalExpression removeAllObjects];
	} else if([operation isEqual:@"Solve!"]) {
		if([internalExpression count] && ![[internalExpression objectAtIndex:[internalExpression count]-1] isEqual:@"="]) {
			[self performOperation:@"="];
		}
		NSArray *keys = [NSArray arrayWithObjects:@"x", @"a", @"b", nil];
		NSArray *values = [NSArray arrayWithObjects:[NSNumber numberWithDouble:2],[NSNumber numberWithDouble:4],[NSNumber numberWithDouble:6],nil];
		NSDictionary *variables = [NSDictionary dictionaryWithObjects:values forKeys:keys];
		operand = [CalculatorBrain evaluateExpression:self.expression usingVariableValues:variables];
	} else {
		[self performWaitingOperation];
		[waitingOperation release];
		waitingOperation = [operation retain];
		waitingOperand = operand;
		[internalExpression addObject:operation];
	}

	return operand;
}

+ (NSString *)descriptionOfExpression:(id)anExpression {
	NSMutableString *result = [[NSMutableString alloc] init];
	for(id obj in anExpression) {
		if([obj isKindOfClass:[NSNumber class]]) {
			[result appendFormat:@"%g ", [obj doubleValue]];
		} else if ([obj length] > 1 && [obj characterAtIndex:0] == '%') {
			[result appendFormat:@"%@ ", [obj substringFromIndex:1]];
		} else {
			[result appendFormat:@"%@ ", obj];
		}
	}
	[result autorelease];
	return [NSString stringWithString:result];
}

- (id)expression {
	return [[internalExpression copy] autorelease];
}





+ (double)evaluateExpression:(id)anExpression
		 usingVariableValues:(NSDictionary *)variables{
	CalculatorBrain *tempBrain = [[CalculatorBrain alloc] init];
	for (id obj in anExpression) {
		if ([obj isKindOfClass:[NSNumber class]] ) {
			tempBrain.operand = [obj doubleValue];
		} else if ([obj isKindOfClass:[NSString class]] ) {
			if ( [obj length] > 1 && [obj characterAtIndex:0] == '%') {
				NSNumber *value = [variables objectForKey:[NSString stringWithFormat:@"%c",[obj characterAtIndex:1]]];
				tempBrain.operand = [value doubleValue];
			} else {
				[tempBrain performOperation:obj];
			}
		}
	}
	
	double result = tempBrain.operand;
	[tempBrain release];
	return result;
}

+ (NSSet *)variablesInExpression:(id)anExpression{
	NSMutableSet *result = [[NSMutableSet alloc] init];
	
	for ( id obj in anExpression ) {
		if ([obj isKindOfClass:[NSString class]]) {
			if([obj length] && [obj characterAtIndex:0]=='%') {
				[result addObject:obj];
			}
		}
	}
	
	[result autorelease];
	return [result count] ? [NSSet setWithSet:result] : nil;
}

+ (id)propertyListForExpression:(id)anExpression {
	return [[anExpression copy] autorelease];
}

+ (id)expressionForPropertyList:(id)propertyList { 
	return [[propertyList copy] autorelease];
}



- (void) dealloc {
	[waitingOperation release];
	[internalExpression release];
	[super dealloc];
}

@end
